﻿using Sai.Core.Introspection;
using Sai.Core.Reporting;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Xml.Serialization;

namespace Sai.Core
{
    [XmlRoot("TypeListing")]
    public class TypeListingByNamespace : ITypeListing
    {
        private Identifier ident = new Identifier();

        private List<NamespaceListing> _namespaces = new List<NamespaceListing>();

        /// <summary>
        /// Identifier used to link back to the summary page.
        /// </summary>
        [XmlAttribute("SummaryID")]
        public string SummaryIdentifier { get; set; }

        /// <summary>
        /// Name used to link back to the summary page.
        /// </summary>
        [XmlAttribute("SummaryName")]
        public string SummaryName { get; set; }

        /// <summary>
        /// Identifier for this listing.
        /// </summary>
        [XmlAttribute("ID")]
        public string Identifier
        {
            get
            {
                return this.ident.Value;
            }

            set
            {
                this.ident = new Identifier(value);
            }
        }

        [XmlAttribute("Name")]
        public string Name
        {
            get;
            set;
        }


        /// <summary>
        /// The link to the app's web page (used to generate footer documentation).
        /// </summary>
        [XmlAttribute("ProductLink")]
        public string ProductLink
        {
            get;
            set;
        }

        /// <summary>
        /// The current version of the app (used to generate footer documentation).
        /// </summary>
        [XmlAttribute("ProductVersion")]
        public string ProductVersion
        {
            get;
            set;
        }

        
        /// <summary>
        /// Namespaces present.
        /// </summary>
        [XmlArray("Namespaces", IsNullable = false)]
        [XmlArrayItem("Namespace", typeof(NamespaceListing))]
        public Collection<NamespaceListing> Namespaces
        {
            get
            {
                if (this._namespaces.Any())
                {
                    return new Collection<NamespaceListing>(this._namespaces);
                }

                return null;
            }
        }

        public void Add(IEnumerable<TypeName> types)
        {
            foreach (var type in types)
            {
                var nsl = this._namespaces.FirstOrDefault(x => x.Name == type.Namespace);

                if (nsl == null)
                {
                    nsl = new NamespaceListing
                    {
                        Name = type.Namespace
                    };

                    this._namespaces.Add(nsl);
                }

                nsl.Add(type);
            }
        }

        public IEnumerable<string> NamespaceNames()
        {
            var ns = new HashSet<string>();

            foreach (var n in this._namespaces)
            {
                ns.Add(n.Name);
            }

            return ns;
        }

        public IEnumerable<TypeName> TypesInNamespace(string namespaceName)
        {
            var nsl = this._namespaces.FirstOrDefault(x => x.Name == namespaceName);

            return nsl.Types;
            //return this._types.Where(x => x.Namespace == namespaceName);
        }

        public IEnumerable<TypeName> AllTypes()
        {
            List<TypeName> types = new List<TypeName>();
            
            foreach (var n in this._namespaces)
            {
                types.AddRange(n.Types);
            }

            return types;
        }

        public void SaveTo(TextWriter writer)
        {
            ObjectFlattener<TypeListingByNamespace>.Flatten(this, writer);
        }

        public string ToXml()
        {
            using (var writer = new StringWriter())
            {
                this.SaveTo(writer);
                return writer.ToString();
            }
        }
    }
}
